package com.core.toolkit.jwt;

import com.core.pojo.Result.Result;
import com.core.pojo.User.dto.UserInfoDTO;
import com.core.pojo.User.entity.User;
import com.core.pojo.User.entity.UserContext;
import com.core.service.User.UserLoginService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.naming.AuthenticationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler.PrintlnLogErrorHandler.log;
import static com.core.constant.RedisConstants.USER_LOGOUT_TOKEN_KEY;
import static com.core.toolkit.jwt.JwtUtil.TOKEN_PREFIX;

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final UserLoginService userLoginService;
    private final StringRedisTemplate stringRedisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        // 1. 从Header获取Token（这里的header是带有"Bearer "的）
        String header = request.getHeader("Authorization");
        if (header == null || !header.startsWith("Bearer ")) {
            // 如果token为空，直接放行，后续由Spring Security的权限控制进行处理（比如允许匿名访问的接口）
            filterChain.doFilter(request, response);
            return;
        }

        try {
            // 2. 解析Token，验证有效性以及获取用户ID
            // 检查是否已经退出登录
            if(Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(USER_LOGOUT_TOKEN_KEY, header))) {
                throw new AuthenticationException("该用户已退出登录");
            }
            // 检查token是否有效
            UserInfoDTO userInfoDTO=JwtUtil.parseJwtToken(header);
            if (userInfoDTO == null){
                throw new AuthenticationException("token用户信息为空");
            }


            // 3. 从Redis获取完整用户信息
            User user = userLoginService.queryUserById(userInfoDTO.getId());
            if(user == null){
                throw new AuthenticationException("用户会话已过期，请重新登陆");
            }

            // 4. 设置Spring Security上下文（这一段是必要的，不然Spring Security不知道你有没有认证成功）
            // 创建用户权限（根据identity确定角色）
            List<GrantedAuthority> authorities = new ArrayList<>();
            if (userInfoDTO.getIdentity() == 1) {
                authorities.add(new SimpleGrantedAuthority("ROLE_STUDENT"));
            } else if (userInfoDTO.getIdentity() == 2) {
                authorities.add(new SimpleGrantedAuthority("ROLE_TEACHER"));
            }
            // 创建认证对象
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(
                            userInfoDTO,
                            null,
                            authorities
                    );
            // 设置到安全上下文
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 5. 设置用户信息到UserContext
            UserContext.setUser(userInfoDTO);
            log.info("用户:{} 已设置到上下文", userInfoDTO.getId());

            // 6. 刷新Redis有效期
            userLoginService.refreshUserTokenTtl(userInfoDTO.getId());
        } catch (AuthenticationException e) {
            log.warn("认证失败: {}", e.getMessage());
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
            return;
        } catch (Exception e) {
            log.error("认证过程中发生错误: {}", e.getMessage());
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "认证服务异常");
            return;
        }


        try {
            filterChain.doFilter(request, response);
        } finally {
            // 确保在请求完成后清除两个上下文
            SecurityContextHolder.clearContext();
            UserContext.removeUser();
            log.debug("上下文已清除");
        }
    }

    // 简单的认证异常类
    public static class AuthenticationException extends Exception {
        public AuthenticationException(String message) {
            super(message);
        }
    }

}
